﻿/*
 * Copyright (c) HiSilicon (Shanghai) Technologies Co., Ltd. 2023-2023. All rights reserved.
 */

#ifndef COMPATIBLE_CMD_H
#define COMPATIBLE_CMD_H

#include "Global.h"
#include "../platform/Securec/securec.h"
namespace TlvLength {
    const int TLV_4 = 4;
}

namespace BurnToolCommon {
struct DIAG_CMD_ID_CONVERT_PACKET_ENCAPSULATION_CLASS {
public:
    DIAG_CMD_ID_CONVERT_PACKET_ENCAPSULATION_CLASS()
        : data(nullptr),
          dataSize(0),
          buffer(nullptr),
          bufferSize(0)
    {
    }
    virtual ~DIAG_CMD_ID_CONVERT_PACKET_ENCAPSULATION_CLASS()
    {
    }
public:
    virtual void Init(PBYTE input, uint32_t inputLen)
    {
        Q_UNUSED(input);
        Q_UNUSED(inputLen);
    }

    virtual void SetData(const PBYTE mData, uint32_t mDataSize)
    {
        Q_UNUSED(mData);
        Q_UNUSED(mDataSize);
    }

    virtual PBYTE TLVPacket(BYTE type)
    {
        Q_UNUSED(type);
        return nullptr;
    }

    virtual uint32_t GetBufferSize()
    {
        return 0;
    }

    virtual PBYTE GetData()
    {
        return data;
    }

    virtual uint32_t GetDataSize()
    {
        return dataSize;
    }
protected:
    PBYTE data;
    uint32_t dataSize;
    PBYTE buffer;
    uint32_t bufferSize;
};

struct DIAG_CMD_ID_CONVERT_PACKET_ENCAPSULATION_CLASS_0XF9 : DIAG_CMD_ID_CONVERT_PACKET_ENCAPSULATION_CLASS {
public:
    DIAG_CMD_ID_CONVERT_PACKET_ENCAPSULATION_CLASS_0XF9() {}
    ~DIAG_CMD_ID_CONVERT_PACKET_ENCAPSULATION_CLASS_0XF9() override
    {
        if (buffer != nullptr) {
            delete[] buffer;
            buffer = nullptr;
        }
        if (data != nullptr) {
            delete []data;
            data = nullptr;
        }
    }

    void Init(PBYTE input, uint32_t inputLen) override
    {
        if (input == nullptr || inputLen == 0) {
            return;
        }
        if (data != nullptr) {
            delete []data;
            data = nullptr;
        }
        mType = input[0] & 0x7f;
        BYTE len = input[1];
        if ((len & 0x80) != 0) {
            dataSize = len & 0x7f;
            data = new BYTE[dataSize]();
            if (data != nullptr) {
                if (dataSize + 1 + 1 > inputLen) {
                    delete []data;
                    data = nullptr;
                    dataSize = 0;
                    return;
                }
                errno_t errMemcpy = memcpy_s(data, dataSize, input + 2, dataSize);
                if (errMemcpy != EOK) {
                    return;
                }
            }
        } else {
            if (len > sizeof (unsigned int)) {
                return;
            }
            dataSize = 0;
            errno_t errMemcpy = memcpy_s(&dataSize, sizeof (unsigned int), buffer + 2, len);
            if (errMemcpy != EOK) {
                return;
            }
            unsigned int maxLen = 0;
            for (BYTE i = 0; i < len; i++) {
                maxLen = (maxLen << 8) | 0xff; // 8
            }
            if (dataSize < 0x7f || dataSize > maxLen) {
                return;
            }
            data = new BYTE[dataSize]();
            if (data != nullptr) {
                if (dataSize + 1 + 1 + len > inputLen) {
                    delete []data;
                    data = nullptr;
                    dataSize = 0;
                    return;
                }
                errMemcpy = memcpy_s(data, dataSize, input + 2 + len, dataSize); // 2
                if (errMemcpy != EOK) {
                    return;
                }
            }
        }
        return;
    }

    void SetData(const PBYTE data, uint32_t dataSize) override
    {
        if (this->data) {
            delete[] this->data;
            this->data = nullptr;
        }
        this->dataSize = dataSize;
        if (this->dataSize > 0xFFFF) {
            return;
        }
        if (data == nullptr || dataSize == 0) {
            this->data = nullptr;
            return;
        }
        this->data = new BYTE[dataSize]();
        if (this->data == nullptr) {
            return;
        }
        errno_t errMemcpy = memcpy_s(this->data, dataSize, data, dataSize);
        if (errMemcpy != EOK) {
            return;
        }
    }

    uint32_t GetBufferSize() override
    {
        return bufferSize;
    }

    PBYTE TLVPacket(BYTE type) override
    {
        if (buffer != nullptr) {
            delete[] buffer;
            buffer = nullptr;
        }
        bufferSize = dataSize + TlvLength::TLV_4;
        BYTE len = 0;
        buffer = new uint8_t[bufferSize]();
        if (buffer == nullptr) {
            return nullptr;
        }
        buffer[0] = (0x80 | type) & 0xff;
        int offset = 2;
        if (dataSize < 0x80) {
            len = (0x80 | dataSize) & 0xff;
            buffer[1] = len;
        } else if (dataSize >= 0x80 && dataSize < 0xFF) {
            len = 1;
            BYTE lenth = dataSize & 0xff;
            buffer[1] = len;
            errno_t errMemcpy = memcpy_s(buffer + 2, len, &lenth, sizeof (BYTE));
            if (errMemcpy != EOK) {
                return nullptr;
            }
            offset += len;
        } else if (dataSize >= 0xFF && dataSize < 0xFFFF) {
            len = 2; // 2
            buffer[1] = len;
            short lenth = dataSize & 0xffff;
            errno_t errMemcpy = memcpy_s(buffer + 2, len, &lenth, sizeof (short));
            if (errMemcpy != EOK) {
                return nullptr;
            }
            offset += len;
        }
        if (data == nullptr || dataSize == 0) {
            return buffer;
        }
        errno_t errMemcpy = memcpy_s(buffer + offset, bufferSize - offset, data, dataSize);
        if (errMemcpy != EOK) {
            return nullptr;
        }
        return buffer;
    }

private:
    uint8_t mType = 0;
};

struct DIAG_CMD_ID_CONVERT_PACKET_ENCAPSULATION_FACTORY {
public:
    DIAG_CMD_ID_CONVERT_PACKET_ENCAPSULATION_FACTORY()
        : type(Global::currentChipTypeId),
          cmdIdPacketClass(GetItem())
    {
    }
    ~DIAG_CMD_ID_CONVERT_PACKET_ENCAPSULATION_FACTORY()
    {
        if (cmdIdPacketClass != nullptr) {
            delete cmdIdPacketClass;
            cmdIdPacketClass = nullptr;
        }
    }

    DIAG_CMD_ID_CONVERT_PACKET_ENCAPSULATION_CLASS *GetCmdIdPacketClass()
    {
        if (cmdIdPacketClass == nullptr) {
            static DIAG_CMD_ID_CONVERT_PACKET_ENCAPSULATION_CLASS stCmdIdPacketClass;
            return &stCmdIdPacketClass;
        }
        return cmdIdPacketClass;
    }
    DIAG_CMD_ID_CONVERT_PACKET_ENCAPSULATION_CLASS *GetItem() const
    {
        switch (type) {
            case ChipType::SLEBS2X:
            case ChipType::SLE2982:
                return new DIAG_CMD_ID_CONVERT_PACKET_ENCAPSULATION_CLASS_0XF9();
            default:
                return new DIAG_CMD_ID_CONVERT_PACKET_ENCAPSULATION_CLASS_0XF9();
        }
    }

    DIAG_CMD_ID_CONVERT_PACKET_ENCAPSULATION_CLASS *GetItem(ChipType chipVersion) const
    {
        switch (chipVersion) {
            case ChipType::SLEBS2X:
            case ChipType::SLE2982:
                return new DIAG_CMD_ID_CONVERT_PACKET_ENCAPSULATION_CLASS_0XF9();
            default:
                return new DIAG_CMD_ID_CONVERT_PACKET_ENCAPSULATION_CLASS_0XF9();
        }
    }
private:
    ChipType type;
    DIAG_CMD_ID_CONVERT_PACKET_ENCAPSULATION_CLASS *cmdIdPacketClass;
};
}

using DIAG_CMD_ID_CONVERT_PACKET_ENCAPSULATION_FACTORY =
    BurnToolCommon::DIAG_CMD_ID_CONVERT_PACKET_ENCAPSULATION_FACTORY;
using DIAG_CMD_ID_CONVERT_PACKET_ENCAPSULATION_CLASS = BurnToolCommon::DIAG_CMD_ID_CONVERT_PACKET_ENCAPSULATION_CLASS;
#endif // __SOP_H__

